主进程如何捕获未处理异常?如何做崩溃守护与自动拉起?
考察点
- Node 异常兜底机制
- agent 类应用的高可用设计(崩溃日志 + 自动恢复)
参考答案
1. 异常捕获
// 同步未捕获异常
process.on('uncaughtException', (err) => {
logger.error('主进程未捕获异常:', err);
// 只记录日志 + 优雅退出,不要吞掉异常继续跑(状态已不可信)
gracefulExit(1);
});
// Promise 未处理的 rejection
process.on('unhandledRejection', (reason) => {
logger.error('未处理的 Promise rejection:', reason);
});
关键认知:uncaughtException 触发后进程状态已不可靠,正确姿势是记录日志后优雅退出,靠守护机制拉起,而不是 catch 住当没事发生。
2. 崩溃日志采集
const { crashReporter } = require('electron');
crashReporter.start({
productName: 'MyAgent',
companyName: 'MyCompany',
submitURL: 'https://crash.example.com/minidump',
uploadToServer: true, // minidump 崩溃转储文件自动上报
});
crashReporter采集的是 Chromium 层崩溃(minidump),与 JS 异常互补- 配合 breakpad 符号表做堆栈符号化(或用 Sentry 的 Electron SDK 一站搞定)
3. 崩溃守护 / 自动拉起
方案一:父子进程守护(自研常用)
// watchdog 进程:启动时 fork 真正的工作进程
const { fork } = require('child_process');
function startWorker() {
const worker = fork(path.join(__dirname, 'worker.js'));
worker.on('exit', (code) => {
if (code !== 0) {
logger.warn(`工作进程异常退出 code=${code},3 秒后重启`);
setTimeout(startWorker, 3000); // 延迟重启,避免崩溃风暴
}
});
}
方案二:借助系统能力
- Windows:注册为服务(
node-windows),由 SCM 负责拉起 - macOS:
launchd的KeepAlive配置 - Linux:systemd
Restart=always
方案三:Electron 内重载渲染进程 渲染进程崩溃不重启应用,监听事件重载即可:
win.webContents.on('render-process-gone', (e, details) => {
logger.error('渲染进程崩溃:', details.reason);
win.reload(); // 直接重载恢复
});
4. 防崩溃风暴
无论哪种方案都要加:
- 重启频率限制(如 1 分钟内崩溃 3 次则停止拉起,提示用户)
- 崩溃标记位:上次异常退出则在下次启动时进入"安全模式"(禁用可疑模块)
面试话术
我做的 agent 采用「主进程兜底日志 + 优雅退出 + 外部 watchdog 拉起」三层机制:JS 层双事件捕获写日志;崩溃走 minidump 上报并做符号化;拉起限制频率防止崩溃风暴,且带安全模式兜底。
常见追问
- 主进程崩溃时渲染进程会怎样? 整个应用退出(主进程是根),所以渲染侧的心跳/重连只能防渲染进程自己的问题。
- 为什么不用 try/catch 包全局? Node 的异步错误捕获不到,必须靠进程级事件。